home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / Javacup / PR8ADPL7.TAR / productivity_tools / PR8ADPL7 / JFSserver.java < prev    next >
Encoding:
Java Source  |  1996-05-23  |  30.8 KB  |  1,329 lines

  1. // JFSserver.java
  2. // The server for the java file system.
  3. // Some notes about permissions
  4. //  - to read a file        r permissions for that file
  5. //  - to write a file        w permissions for file 
  6. //  - to create a file        w permissions for directory
  7. //  - to delete a file        w permissions for file
  8. //  - to chmod a file        p permissions for file
  9. import java.io.*;
  10. import java.net.*;
  11. import java.util.Vector;
  12. import java.util.Date;
  13. import java.util.Hashtable;
  14. import java.util.Enumeration;
  15. import JFSdirectory;
  16. import CryptConnection;
  17. import FileSystem;
  18.  
  19. public class JFSserver
  20. {
  21.     static LineOutputStream log;        // log file
  22.     static Vector cons = new Vector();    // running connections
  23.     static int cnum = 0;            // connection ID count
  24.  
  25.     static ServerProperty bsent, brecv,    // changing server properties
  26.                   tcon, treq, tdata,
  27.                   tsucc, tfail;
  28.  
  29.     // Properties stored by the server.
  30.     static Vector pcache = new Vector();
  31.  
  32.     // main
  33.     // The server starts here
  34.     public static void main(String argv[])
  35.     {
  36.  
  37.     // Parse command line args
  38.     if (argv.length < 1 || argv.length > 2) {
  39.         System.err.println("usage : java JFSserver <root> [log]");
  40.         System.exit(1);
  41.         }
  42.     String logname = "server_log";
  43.     if (argv.length == 2) {
  44.         logname = argv[1];
  45.         }
  46.     if (!logname.equals("-")) {
  47.         // open log file
  48.         try {
  49.             RandomAccessFile raf =
  50.               new RandomAccessFile(logname, "rw");
  51.             raf.seek(raf.length());
  52.             log = new LineOutputStream(
  53.                 new FileOutputStream(raf.getFD()));
  54.             }
  55.         catch(IOException e) {
  56.             System.err.println("Failed to open log file");
  57.             System.exit(1);
  58.             }
  59.         }
  60.     else {
  61.         // Write to stdout
  62.         log = new LineOutputStream(System.out);
  63.         }
  64.  
  65.     FileSystem.init(argv[0]);
  66.     loginfo("FileSystem started on "+argv[0]);
  67.  
  68.     // Open main socket
  69.     ServerSocket ss = null;
  70.     try
  71.         ss = new ServerSocket(9888);
  72.     catch(IOException e) {
  73.         System.err.println("Failed to open socket : "+e.getMessage());
  74.         System.exit(1);
  75.         }
  76.     loginfo("JFSserver started");
  77.  
  78.     // store changing properties
  79.     putprop(bsent = new ServerProperty("Bytes sent","root","0",false));
  80.     putprop(brecv = new ServerProperty("Bytes received","root","0",false));
  81.     putprop(tcon  = new ServerProperty("Connections","root","0",false));
  82.     putprop(treq = new ServerProperty("Requests","root","0",false));
  83.     putprop(tdata = new ServerProperty("Data replys","root","0",false));
  84.     putprop(tsucc = new ServerProperty("Success replys","root","0",false));
  85.     putprop(tfail = new ServerProperty("Fail replys","root","0",false));
  86.  
  87.     // store contant properties
  88.     String now = new Date(System.currentTimeMillis()).toString();
  89.     putprop(new ServerProperty("Boot time","root",now,false));
  90.     String jpr[] = {"java.version", "Java version",
  91.             "java.vendor", "Java vendor",
  92.             "java.class.version", "Java class version" };
  93.     for(int i=0; i<jpr.length; i+=2) {
  94.         String got = System.getProperty(jpr[i]);
  95.         if (got == null) got = "unknown";
  96.         putprop(new ServerProperty(jpr[i+1], "root", got, false));
  97.         }
  98.  
  99.     // load external properties
  100.     loadcache();
  101.  
  102.     // Start timeout reaper
  103.     new IdleReaper().start();
  104.  
  105.     // Listen for connections, creating new threads to handle each one
  106.     while(true) {
  107.         Socket con = null;
  108.         try
  109.             con = ss.accept();
  110.         catch(IOException e)
  111.             continue;
  112.         loginfo("Got connection from " +
  113.             con.getInetAddress().getHostName());
  114.         new ServerClient(con).start();
  115.         tcon.setval(tcon.getval() + 1);
  116.         }
  117.     }
  118.  
  119.     // getprop
  120.     // Look up a property in the cache
  121.     static synchronized ServerProperty getprop(String name, String user)
  122.     {
  123.     int idx = indexprop(name, user);
  124.     return idx < 0 ? null : (ServerProperty)pcache.elementAt(idx);
  125.     }
  126.  
  127.     // indexprop
  128.     // Returns the index of a property in the cache, or -1
  129.     private static synchronized int indexprop(String name, String user)
  130.     {
  131.     for(int i=0; i<pcache.size(); i++) {
  132.         ServerProperty c = (ServerProperty)pcache.elementAt(i);
  133.         if (c.name.equals(name) && c.user.equals(user))
  134.             return i;
  135.         }
  136.     return -1;
  137.     }
  138.  
  139.     // putprop
  140.     // Write a property into the cache, and if external flush the
  141.     // cache to /etc/properties
  142.     static synchronized void putprop(ServerProperty n)
  143.     {
  144.     int old = indexprop(n.name, n.user);
  145.     if (old < 0) pcache.addElement(n);
  146.     else pcache.setElementAt(n, old);
  147.     if (n.external) savecache();
  148.     }
  149.  
  150.     // allprops
  151.     // Returns an array of all the properties in the cache
  152.     static synchronized ServerProperty []allprops()
  153.     {
  154.     ServerProperty a[] = new ServerProperty[pcache.size()];
  155.     for(int i=0; i<pcache.size(); i++)
  156.         a[i] = (ServerProperty)pcache.elementAt(i);
  157.     return a;
  158.     }
  159.  
  160.     // delprop
  161.     // Delete the property matching the given name and user
  162.     static synchronized void delprop(String name, String user)
  163.     {
  164.     int idx = indexprop(name, user);
  165.     if (idx != -1) {
  166.         pcache.removeElementAt(idx);
  167.         savecache();
  168.         }
  169.     }
  170.  
  171.     // loadcache
  172.     // Read all the properties from /etc/properties into the cache
  173.     static private void loadcache()
  174.     {
  175.     BufferInputStream buf = null;
  176.     try buf = new BufferInputStream(
  177.            FileSystem.getfile("/etc/properties", 0, -1, -1));
  178.     catch(BadPathException e) {
  179.         loginfo("Failed to read /etc/properties");
  180.         return;
  181.         }
  182.     try {
  183.         while(true)
  184.             pcache.addElement(new ServerProperty(buf.gets()));
  185.         }
  186.     catch(IOException e);
  187.     loginfo("Properties cache started");
  188.     }
  189.  
  190.     // savecache
  191.     // Write the cache out to /etc/properties
  192.     static private synchronized void savecache()
  193.     {
  194.     BufferOutputStream buf = new BufferOutputStream();
  195.     for(int i=0; i<pcache.size(); i++) {
  196.         ServerProperty p = (ServerProperty)pcache.elementAt(i);
  197.         if (p.external)
  198.             buf.puts(p.output());
  199.         }
  200.     try FileSystem.putfile("/etc/properties", 0, buf.getarray(),
  201.                    "root", "text/plain");
  202.     catch(BadPathException e)
  203.         System.err.println("Could not write to /etc/properties : " + 
  204.                    e.getMessage());
  205.     }
  206.  
  207.     // loginfo
  208.     // Write some informative message to the log
  209.     static void loginfo(String s)
  210.     {
  211.     Date d = new Date(System.currentTimeMillis());
  212.     synchronized(log)
  213.         log.puts("-- ["+d+"] "+s);
  214.     }
  215. }
  216.  
  217.  
  218. // ServerClient
  219. // A connection to a JFS client. The client sends requests to the server, in
  220. // the form
  221. //
  222. //   REQUEST = REQTYPE HEADER* '\n' byte*
  223. //
  224. //   REQTYPE = 'Request:' { 'Auth' | 'Get' | 'Put' | 'Stat' | 'Passwd' | 
  225. //           'Chmod' | 'Delete' | 'Purge' | 'Rename' | 'Mkdir' | 'Uinfo' |
  226. //           'Ginfo' | 'Copy' | 'Close' | 'Getprop' | 'Setprop' |
  227. //           'Delprop' | 'Ulist' | 'Purge' | 'Chtype' } '\n'
  228. //
  229. //   HEADER = HeaderName':' HeaderValue '\n'
  230. //
  231. // The server sends replies to the client, in the form
  232. //
  233. //   REPLY = REPTYPE HEADER* '\n' byte*
  234. //
  235. //   REPTYPE = 'Reply:' { 'Success' | 'Fail' | 'Data' } '\n'
  236. //
  237. //   HEADER = HeaderName':' HeaderValue '\n'
  238. //  
  239. class ServerClient extends Thread
  240. {
  241.     String name;        // name of this user, or null
  242.     String host;        // host user is connecting from
  243.     CryptInputStream in;    // connection from client
  244.     CryptOutputStream out;    // connection to client
  245.  
  246.     int cnum;        // connection ID
  247.     int sent;        // bytes sent on this connection
  248.     int recv;        // bytes received on this connection
  249.     long last;        // time of last activity
  250.  
  251.     ServerClient(Socket s)
  252.     {
  253.     synchronized(JFSserver.cons)
  254.         JFSserver.cons.addElement(this);
  255.     cnum = JFSserver.cnum++;
  256.     try {
  257.         CryptConnection con = new CryptConnection(s.getInputStream(),
  258.                               s.getOutputStream());
  259.         in = con.getInputStream();
  260.         out = con.getOutputStream();
  261.         }
  262.     catch(IOException e);
  263.     host = s.getInetAddress().getHostName();
  264.     last = System.currentTimeMillis();
  265.     }
  266.  
  267.     // run
  268.     // Forever read requests from this client, service them and send
  269.     // back replys. A client must send an Auth request with a valid name
  270.     // and password before doing any other requests.
  271.     public void run()
  272.     {
  273.     while(true) {
  274.         Message msg = null;
  275.         try msg = new Message(in);
  276.         catch(IOException e) {
  277.             break;
  278.             }
  279.         String req = msg.find("Request");
  280.         if (req == null) {
  281.             failreply("No Request header");
  282.             continue;
  283.             }
  284.  
  285.         // Find out what the request is, and handle it
  286.         logrequest(msg);
  287.         if (req.equals("Auth"))
  288.             auth(msg);
  289.         else if (req.equals("Close"))
  290.             break;
  291.         else {
  292.             if (name == null) {
  293.                 failreply("Unauthenticated");
  294.                 continue;
  295.                 }
  296.             if (req.equals("Get"))
  297.                 get(msg);
  298.             else if (req.equals("Put"))
  299.                 put(msg);
  300.             else if (req.equals("Passwd"))
  301.                 passwd(msg);
  302.             else if (req.equals("Stat"))
  303.                 stat(msg);
  304.             else if (req.equals("Chmod"))
  305.                 chmod(msg);
  306.             else if (req.equals("Delete"))
  307.                 delete(msg);
  308.             else if (req.equals("Rename"))
  309.                 rename(msg);
  310.             else if (req.equals("Mkdir"))
  311.                 mkdir(msg);
  312.             else if (req.equals("Uinfo"))
  313.                 uinfo(msg);
  314.             else if (req.equals("Ginfo"))
  315.                 ginfo(msg);
  316.             else if (req.equals("Copy"))
  317.                 copy(msg);
  318.             else if (req.equals("Getprop"))
  319.                 getprop(msg);
  320.             else if (req.equals("Putprop"))
  321.                 putprop(msg);
  322.             else if (req.equals("Delprop"))
  323.                 delprop(msg);
  324.             else if (req.equals("Ulist"))
  325.                 ulist(msg);
  326.             else if (req.equals("Purge"))
  327.                 purge(msg);
  328.             else if (req.equals("Chtype"))
  329.                 chtype(msg);
  330.             else
  331.                 failreply("Unknown request");
  332.             }
  333.         }
  334.     try {
  335.         in.close();
  336.         out.close();
  337.         }
  338.     catch(IOException e);
  339.     JFSserver.loginfo("Closed connection to "+host);
  340.     synchronized(JFSserver.cons)
  341.         JFSserver.cons.removeElement(this);
  342.     }
  343.  
  344.     // shutdown
  345.     // Disconnect this client
  346.     void shutdown()
  347.     {
  348.     try {
  349.         in.close();
  350.         out.close();
  351.         }
  352.     catch(IOException e);
  353.     }
  354.  
  355.     // auth
  356.     // Process an Auth request. Valid headers are:
  357.     // 'Username:' Name
  358.     // 'Password:' Password
  359.     void auth(Message msg)
  360.     {
  361.     String authname = msg.find("Username");
  362.     String authpass = msg.find("Password");
  363.     if (authname == null) {
  364.         failreply("No Username given");
  365.         return;
  366.         }
  367.     if (authpass == null) {
  368.         failreply("No Password given");
  369.         return;
  370.         }
  371.     JFSuser u;
  372.     try u = FileSystem.getuser(authname);
  373.     catch(BadUserException e) {
  374.         failreply("User not found");
  375.         return;
  376.         }
  377.     if (!u.password.equals(authpass)) {
  378.         failreply("Password incorrect");
  379.         return;
  380.         }
  381.     name = authname;
  382.     successreply();
  383.     }
  384.  
  385.     // get
  386.     // Process a Get request. Valid headers are:
  387.     // 'File:' dir
  388.     // 'Start:' start byte    (optional)
  389.     // 'End:' end byte    (only if Start was given)
  390.     void get(Message msg)
  391.     {
  392.     String file = msg.find("File");
  393.     String start = msg.find("Start");
  394.     String end = msg.find("End");
  395.     if (file == null) {
  396.         failreply("No File given");
  397.         return;
  398.         }
  399.     if (start != null && end == null) {
  400.         failreply("No End given");
  401.         return;
  402.         }
  403.  
  404.     // does the file exist, and can this user access it?
  405.     JFSfile fileinfo;
  406.     try fileinfo = accessfile(file, "r");
  407.     catch(BadPathException e) {
  408.         failreply(e.getMessage());
  409.         return;
  410.         }
  411.     if (fileinfo == null) {
  412.         failreply("File not found");
  413.         return;
  414.         }
  415.  
  416.     Message reply;
  417.     if (fileinfo.type.equals("jfs/directory")) {
  418.         // send directory to user
  419.         reply = new Message();
  420.         reply.add("Reply","Data");
  421.         JFSdirectory listdir;
  422.         try listdir = FileSystem.getdir(file);
  423.         catch(BadPathException e) {
  424.             failreply("Couldn't list directory");
  425.             return;
  426.             }
  427.         BufferOutputStream buf = new BufferOutputStream();
  428.         listdir.output(buf);
  429.         reply.setdata(buf.getarray());
  430.         }
  431.     else if (fileinfo.type.equals("jfs/special")) {
  432.         // This is a device file. Call the appropriate driver to
  433.         // read from it.
  434.         DeviceDriver drv = null;
  435.         try {
  436.             Class dc = Class.forName(fileinfo.name+"Device");
  437.             drv = (DeviceDriver)dc.newInstance();
  438.             }
  439.         catch(Exception e) {
  440.             failreply(e.getClass().getName()+" loading "+
  441.                   "device driver");
  442.             return;
  443.             }
  444.         try reply = drv.read(msg, this);
  445.         catch(IOException e) {
  446.             failreply("Error reading from device driver : "+
  447.                   e.getMessage());
  448.             return;
  449.             }
  450.         }
  451.     else {
  452.         // send file to user
  453.         reply = new Message();
  454.         reply.add("Reply","Data");
  455.         int ver = 0;
  456.         if (fileinfo.multiversion) {
  457.             String version = msg.find("Version");
  458.             if (version != null) {
  459.                 // Use given version number
  460.                 if ((ver = Integer.parseInt(version)) < 1) {
  461.                     failreply("Invalid version number");
  462.                     return;
  463.                     }
  464.                 }
  465.             else {
  466.                 // Use latest version
  467.                 for(int i=0; i<fileinfo.versions.size(); i++) {
  468.                     JFSversion vi = (JFSversion)fileinfo.
  469.                             versions.elementAt(i);
  470.                     if (vi.number > ver)
  471.                         ver = vi.number;
  472.                     }
  473.                 }
  474.             }
  475.         byte data[] = null;
  476.         int st = -1, en = -1;
  477.         if (start != null) {
  478.             st = Integer.parseInt(start);
  479.             en = Integer.parseInt(end);
  480.             if (st < 0 || en < 0 || en < st) {
  481.                 failreply("Invalid file section");
  482.                 return;
  483.                 }
  484.             }
  485.         try data = FileSystem.getfile(file, ver, st, en);
  486.         catch(BadPathException e) {
  487.             failreply("Couldn't read file");
  488.             return;
  489.             }
  490.         reply.setdata(data);
  491.         }
  492.     reply.send(out);
  493.     logreply(reply);
  494.     }
  495.  
  496.     // put
  497.     // Process a Put request. Valid headers are:
  498.     // 'File:' filename
  499.     // 'Version:' number        (optional)
  500.     // 'Content-type:' mimetype
  501.     // 'Content-length:' size
  502.     // Followed by size bytes of data
  503.     void put(Message msg)
  504.     {
  505.     String file = msg.find("File");
  506.     String version = msg.find("Version");
  507.     String type = msg.find("Content-type");
  508.     if (file == null) {
  509.         failreply("No File given");
  510.         return;
  511.         }
  512.     if (type == null)
  513.         type = "application/octet-stream";
  514.     if (msg.getdata() == null) {
  515.         // If no data given, assume 0 bytes
  516.         msg.setdata(new byte[0]);
  517.         }
  518.  
  519.     // Is the mime type given sensible?
  520.     MimeType mt = new MimeType(type);
  521.     if (!mt.exact() || (mt.maj.equals("jfs") && !name.equals("root"))) {
  522.         failreply("Invalid mime type");
  523.         return;
  524.         }
  525.  
  526.     // Can this user write to the file (if it exists) or it's
  527.     // directory (if it doesn't) ?
  528.     JFSuser u = null;
  529.     try u = FileSystem.getuser(name);
  530.     catch(BadUserException e) {
  531.         failreply("You don't exist");
  532.         }
  533.     JFSfile fileinfo = null;
  534.     try {
  535.         fileinfo = FileSystem.statfile(file);
  536.         if (fileinfo.type.equals("jfs/directory")) {
  537.             failreply("Cannot overwrite directory");
  538.             return;
  539.             }
  540.         if (u.perms(fileinfo).indexOf('w') < 0) {
  541.             failreply("Permission denied for file");
  542.             return;
  543.             }
  544.         }
  545.     catch(BadPathException e);
  546.     if (fileinfo == null) {
  547.         try {
  548.             JFSfile parinfo =
  549.                        FileSystem.statfile(FileSystem.parent(file));
  550.             if (u.perms(parinfo).indexOf('w') < 0) {
  551.                 failreply("Permission denied for directory");
  552.                 return;
  553.                 }
  554.             }
  555.         catch(BadPathException e) {
  556.             failreply("Parent directory not found");
  557.             return;
  558.             }
  559.         }
  560.  
  561.     if (fileinfo != null && fileinfo.type.equals("jfs/special")) {
  562.         // This is a device file. Call the appropriate device driver.
  563.         DeviceDriver drv = null;
  564.         try {
  565.             Class dc = Class.forName(fileinfo.name+"Device");
  566.             drv = (DeviceDriver)dc.newInstance();
  567.             }
  568.         catch(Exception e) {
  569.             failreply(e.getClass().getName()+" loading "+
  570.                   "device driver");
  571.             return;
  572.             }
  573.         try drv.write(msg, this);
  574.         catch(IOException e) {
  575.             failreply("Error writing to device driver : "+
  576.                   e.getMessage());
  577.             return;
  578.             }
  579.         successreply();
  580.         }
  581.     else {
  582.         // This is a normal file. Write into it
  583.         try {
  584.             int v = version==null ? 0 : Integer.parseInt(version);
  585.             FileSystem.putfile(file, v, msg.getdata(), u.name,
  586.                        mt.toString());
  587.             }
  588.         catch(BadPathException e) {
  589.             failreply("Error saving file");
  590.             return;
  591.             }
  592.         successreply();
  593.         }
  594.     }
  595.  
  596.     // passwd
  597.     // Process a Passwd request. Valid headers are:
  598.     // 'Old:' oldpass
  599.     // 'New:' newpass
  600.     void passwd(Message msg)
  601.     {
  602.     }
  603.  
  604.     // stat
  605.     // Process a Stat request. Valid headers are:
  606.     // 'File:' filename
  607.     void stat(Message msg)
  608.     {
  609.     String file = msg.find("File");
  610.     if (file == null) {
  611.         failreply("No File given");
  612.         return;
  613.         }
  614.  
  615.     try accessparent(file, 'r');
  616.     catch(BadPathException e) {
  617.         failreply(e.getMessage());
  618.         return;
  619.         }
  620.  
  621.     // Does this file exist?
  622.     JFSfile fileinfo = null;
  623.     try fileinfo = FileSystem.statfile(file);
  624.     catch(BadPathException e) {
  625.         failreply("Couldn't stat file");
  626.         return;
  627.         }
  628.     JFSdirectory tmpdir = new JFSdirectory();
  629.     tmpdir.add(fileinfo);
  630.     BufferOutputStream buf = new BufferOutputStream();
  631.     tmpdir.output(buf);
  632.     Message reply = new Message();
  633.     reply.add("Reply","Data");
  634.     reply.setdata(buf.getarray());
  635.     reply.send(out);
  636.     logreply(reply);
  637.     }
  638.  
  639.     // chmod
  640.     // Process a Chmod request. Valid headers are:
  641.     // 'File:' filename
  642.     // 'User:' user/groupname
  643.     // 'Perms:' permissions    (optional)
  644.     void chmod(Message msg)
  645.     {
  646.     String file = msg.find("File");
  647.     String user = msg.find("User");
  648.     String perms = msg.find("Perms");
  649.     if (file == null) {
  650.         failreply("No File given");
  651.         return;
  652.         }
  653.     if (user == null) {
  654.         failreply("No User given");
  655.         return;
  656.         }
  657.  
  658.     // Can this user change permissions on this file?
  659.     JFSfile fileinfo = null;
  660.     try fileinfo = accessfile(file, "p");
  661.     catch(BadPathException e) {
  662.         failreply(e.getMessage());
  663.         return;
  664.         }
  665.     if (fileinfo == null) {
  666.         failreply("File not found");
  667.         return;
  668.         }
  669.  
  670.     // Change the permissions
  671.     try FileSystem.chmod(file, user, perms);
  672.     catch(BadPathException e) {
  673.         failreply(e.getMessage());
  674.         return;
  675.         }
  676.     successreply();
  677.     }
  678.  
  679.     // delete
  680.     // Process a Delete request. Valid headers are:
  681.     // 'File:' filename
  682.     // 'Version:' number    (optional)
  683.     void delete(Message msg)
  684.     {
  685.     String file = msg.find("File");
  686.     String version = msg.find("Version");
  687.     if (file == null) {
  688.         failreply("No File given");
  689.         return;
  690.         }
  691.  
  692.     // can this user access the file?
  693.     try accessfile(file, "w");
  694.     catch(BadPathException e) {
  695.         failreply(e.getMessage());
  696.         return;
  697.         }
  698.  
  699.     // Delete the file
  700.     int v = version == null ? 0 : Integer.parseInt(version);
  701.     try FileSystem.delete(file, v);
  702.     catch(BadPathException e) {
  703.         failreply("File not found");
  704.         return;
  705.         }
  706.     successreply();
  707.     }
  708.  
  709.     // rename
  710.     // Process a Rename request. Valid headers are
  711.     // 'Source:' sourcefile
  712.     // 'Destination:' destfile
  713.     void rename(Message msg)
  714.     {
  715.     String src = msg.find("Source");
  716.     String dst = msg.find("Destination");
  717.     if (src == null) {
  718.         failreply("No Source given");
  719.         return;
  720.         }
  721.     if (dst == null) {
  722.         failreply("No Destination given");
  723.         return;
  724.         }
  725.  
  726.     // Can we access the source file for reading & writing?
  727.     JFSfile srcinfo = null;
  728.     try srcinfo = accessfile(src, "rw");
  729.     catch(BadPathException e) {
  730.         failreply(e.getMessage()+" for source file");
  731.         return;
  732.         }
  733.  
  734.     // if the dest file exists, can we access it? and if it doesn't can
  735.     // we access the dir it's in?
  736.     JFSfile dstinfo = null;
  737.     try dstinfo = accessfile(dst, "w");
  738.     catch(BadPathException e) {
  739.         failreply(e.getMessage()+" for destination file");
  740.         return;
  741.         }
  742.     if (dstinfo == null) {
  743.         try accessparent(dst, 'w');
  744.         catch(BadPathException e) {
  745.             failreply(e.getMessage()+" for destination dir");
  746.             return;
  747.             }
  748.         }
  749.  
  750.     // do the renaming
  751.     try FileSystem.rename(src, dst, name);
  752.     catch(BadPathException e) {
  753.         failreply(e.getMessage());
  754.         return;
  755.         }
  756.     successreply();
  757.     }
  758.  
  759.     // mkdir
  760.     // Process a Mkdir request. Valid headers are:
  761.     // 'Directory:' pathname
  762.     void mkdir(Message msg)
  763.     {
  764.     String dir = msg.find("Directory");
  765.     if (dir == null) {
  766.         failreply("No Directory given");
  767.         return;
  768.         }
  769.  
  770.     // Can we access this dir's parent?
  771.     try accessparent(dir, 'w');
  772.     catch(BadPathException e) {
  773.         failreply(e.getMessage());
  774.         return;
  775.         }
  776.  
  777.     // Create the directory
  778.     try FileSystem.mkdir(dir, name);
  779.     catch(BadPathException e) {
  780.         failreply("Failed to create directory");
  781.         return;
  782.         }
  783.     successreply();
  784.     }
  785.  
  786.     // uinfo - Process a Uinfo request. Valid headers are:
  787.     // 'Username:' user
  788.     void uinfo(Message msg)
  789.     {
  790.     String user = msg.find("Username");
  791.     if (user == null) {
  792.         failreply("No Username given");
  793.         return;
  794.         }
  795.  
  796.     // get this user's details
  797.     JFSuser u = null;
  798.     try u = FileSystem.getuser(user);
  799.     catch(BadUserException e) {
  800.         failreply("User not found");
  801.         return;
  802.         }
  803.     u.password = "";
  804.  
  805.     // send details to client
  806.     String ustr = u.output();
  807.     byte ubuf[] = new byte[ustr.length()];
  808.     for(int i=0; i<ubuf.length; i++)
  809.         ubuf[i] = (byte)ustr.charAt(i);
  810.     Message r = new Message();
  811.     r.add("Reply","Data");
  812.     r.setdata(ubuf);
  813.     r.send(out);
  814.     logreply(r);
  815.     }
  816.  
  817.     // ginfo
  818.     // Process a Ginfo request. Valid headers are
  819.     // 'Group:' name
  820.     void ginfo(Message msg)
  821.     {
  822.     String group = msg.find("Group");
  823.     if (group == null) {
  824.         failreply("No Group given");
  825.         return;
  826.         }
  827.  
  828.     // get the list of users in this group
  829.     Vector gv = null;
  830.     try gv = FileSystem.getgroup(group);
  831.     catch(BadGroupException e) {
  832.         failreply("Error reading group");
  833.         return;
  834.         }
  835.  
  836.     // send list to user
  837.     BufferOutputStream buf = new BufferOutputStream();
  838.     for(int i=0; i<gv.size(); i++)
  839.         buf.puts((String)gv.elementAt(i));
  840.     Message r = new Message();
  841.     r.add("Reply","Data");
  842.     r.setdata(buf.getarray());
  843.     r.send(out);
  844.     logreply(r);
  845.     }
  846.     
  847.     // copy
  848.     // Process a Copy request. Valid headers are:
  849.     // 'Source:' sourcefile
  850.     // 'Destination:' destfile
  851.     void copy(Message msg)
  852.     {
  853.     String src = msg.find("Source");
  854.     String dst = msg.find("Destination");
  855.     if (src == null) {
  856.         failreply("No Source given");
  857.         return;
  858.         }
  859.     if (dst == null) {
  860.         failreply("No Destination given");
  861.         return;
  862.         }
  863.  
  864.     // can we access the source file?
  865.     JFSfile srcinfo = null;
  866.     try srcinfo = accessfile(src, "r");
  867.     catch(BadPathException e) {
  868.         failreply(e.getMessage()+" for source file");
  869.         return;
  870.         }
  871.  
  872.     // if the dest file exists, can we access it? and if it doesn't can
  873.     // we access the dir it's in?
  874.     JFSfile dstinfo = null;
  875.     try dstinfo = accessfile(dst, "w");
  876.     catch(BadPathException e) {
  877.         failreply(e.getMessage()+" for destination file");
  878.         return;
  879.         }
  880.     if (dstinfo == null) {
  881.         try accessparent(dst, 'w');
  882.         catch(BadPathException e) {
  883.             failreply(e.getMessage()+" for destination dir");
  884.             return;
  885.             }
  886.         }
  887.  
  888.     // do the copy
  889.     try FileSystem.copy(src, dst, name);
  890.     catch(BadPathException e) {
  891.         failreply(e.getMessage());
  892.         return;
  893.         }
  894.     successreply();
  895.     }
  896.  
  897.     // getprop
  898.     // Proces a Getprop request to get one property. Valid headers are:
  899.     // 'Property:' name
  900.     // 'User:' username
  901.     void getprop(Message msg)
  902.     {
  903.     String property = msg.find("Property");
  904.     String user = msg.find("User");
  905.     if (property == null) {
  906.         failreply("No Property given");
  907.         return;
  908.         }
  909.     if (user == null) {
  910.         failreply("No User given");
  911.         return;
  912.         }
  913.     ServerProperty p = JFSserver.getprop(property, name);
  914.     if (p == null) {
  915.         failreply("Property not found");
  916.         return;
  917.         }
  918.     Message r = new Message();
  919.     r.add("Reply","Data");
  920.     String pstr = p.output();
  921.     byte pb[] = new byte[pstr.length()];
  922.     pstr.getBytes(0, pstr.length(), pb, 0);
  923.     r.setdata(pb);
  924.     r.send(out);
  925.     logreply(r);
  926.     }
  927.  
  928.     // putprop
  929.     // Handle a Putprop request. The property to store is passed as data
  930.     // in the message.
  931.     void putprop(Message msg)
  932.     {
  933.     ServerProperty p = null;
  934.     byte data[] = msg.getdata();
  935.     if (data == null) {
  936.         failreply("No data given??");
  937.         return;
  938.         }
  939.     try p = new ServerProperty(new String(msg.getdata(), 0));
  940.     catch(IOException e) {
  941.         failreply("Property format error");
  942.         return;
  943.         }
  944.     if (!p.name.equals(name) && !name.equals("root")) {
  945.         failreply("Permission denied");
  946.         return;
  947.         }
  948.     ServerProperty existing = JFSserver.getprop(p.name, p.user);
  949.     if (existing != null && !existing.external) {
  950.         failreply("Cannot overwrite internal property");
  951.         return;
  952.         }
  953.     JFSserver.putprop(p);
  954.     successreply();
  955.     }
  956.  
  957.     // delprop
  958.     // Handler a Delprop request to delete a server property. Valid
  959.     // headers are:
  960.     //  'Property:' name
  961.     //  'User:' username
  962.     void delprop(Message msg)
  963.     {
  964.     String property = msg.find("Property");
  965.     String user = msg.find("User");
  966.     if (property == null) {
  967.         failreply("No Property given");
  968.         return;
  969.         }
  970.     if (user == null) {
  971.         failreply("No User given");
  972.         return;
  973.         }
  974.     if (!user.equals(name) && !name.equals("root")) {
  975.         failreply("Permission denied");
  976.         return;
  977.         }
  978.     ServerProperty old = JFSserver.getprop(property, user);
  979.     if (old != null && !old.external) {
  980.         failreply("Cannot delete internal properties");
  981.         return;
  982.         }
  983.     JFSserver.delprop(property, user);
  984.     successreply();
  985.     }
  986.  
  987.     // ulist
  988.     // Handle a Ulist request to get all server users (minus passwords).
  989.     void ulist(Message msg)
  990.     {
  991.     // Get /etc/users
  992.     BufferInputStream ibuf = null;
  993.     try ibuf = new BufferInputStream(
  994.            FileSystem.getfile("/etc/users", 0, -1, -1));
  995.     catch(BadPathException e) {
  996.         failreply("Could not read /etc/users : "+e.getMessage());
  997.         return;
  998.         }
  999.  
  1000.     // Write to output buffer
  1001.     BufferOutputStream obuf = new BufferOutputStream();
  1002.     try while(true) {
  1003.         JFSuser u = new JFSuser(ibuf.gets());
  1004.         u.password = "";
  1005.         obuf.puts(u.output());
  1006.         }
  1007.     catch(IOException e);
  1008.  
  1009.     // Return to client
  1010.     Message r = new Message();
  1011.     r.add("Reply", "Data");
  1012.     r.setdata(obuf.getarray());
  1013.     r.send(out);
  1014.     }
  1015.  
  1016.     // purge
  1017.     // Process a Purge request. Valid headers are:
  1018.     //  'File:' filename
  1019.     void purge(Message msg)
  1020.     {
  1021.     String file = msg.find("File");
  1022.     if (file == null) {
  1023.         failreply("No File given");
  1024.         return;
  1025.         }
  1026.  
  1027.     // can this user access the file?
  1028.     JFSfile info = null;
  1029.     try info = accessfile(file, "w");
  1030.     catch(BadPathException e) {
  1031.         failreply(e.getMessage());
  1032.         return;
  1033.         }
  1034.     if (info == null) {
  1035.         failreply("File not found");
  1036.         return;
  1037.         }
  1038.     if (!info.multiversion) {
  1039.         failreply("Cannot purge single version files");
  1040.         return;
  1041.         }
  1042.  
  1043.     // Delete all versions except for the highest
  1044.     Vector vv = info.versions;
  1045.     for(int i=0; i<vv.size()-1; i++) {
  1046.         int vn = ((JFSversion)vv.elementAt(i)).number;
  1047.         try FileSystem.delete(file, vn);
  1048.         catch(BadPathException e) {
  1049.             failreply("Could not delete version "+vn+" : "+
  1050.                   e.getMessage());
  1051.             return;
  1052.             }
  1053.         }
  1054.     successreply();
  1055.     }
  1056.  
  1057.     void chtype(Message msg)
  1058.     {
  1059.     String file = msg.find("File");
  1060.     String type = msg.find("Type");
  1061.     if (file == null) {
  1062.         failreply("No File given");
  1063.         return;
  1064.         }
  1065.     if (type == null) {
  1066.         failreply("No Type given");
  1067.         return;
  1068.         }
  1069.     MimeType mt = new MimeType(type);
  1070.     if (!mt.exact() || (mt.maj.equals("jfs") && !name.equals("root"))) {
  1071.         failreply("Invalid mime type");
  1072.         return;
  1073.         }
  1074.  
  1075.     // Can we access this file?
  1076.     JFSfile info = null;
  1077.     try info = accessfile(file, "w");
  1078.     catch(BadPathException e) {
  1079.         failreply(e.getMessage());
  1080.         return;
  1081.         }
  1082.     if (info == null) {
  1083.         failreply("File not found");
  1084.         return;
  1085.         }
  1086.  
  1087.     // Change the type
  1088.     info.type = type;
  1089.     try FileSystem.putinfo(file, info);
  1090.     catch(BadPathException e) {
  1091.         failreply("Error writing file info : "+e.getMessage());
  1092.         return;
  1093.         }
  1094.     successreply();
  1095.     }
  1096.  
  1097.     // accessparent
  1098.     // Can this user access the given file's parent
  1099.     void accessparent(String file, char mode) throws BadPathException
  1100.     {
  1101.     String par = null;
  1102.     try par = FileSystem.parent(file);
  1103.     catch(BadPathException e)
  1104.         throw new BadPathException("Bad filename");
  1105.     if (!par.equals("")) {
  1106.         JFSfile fileinfo = null;
  1107.         try fileinfo = FileSystem.statfile(par);
  1108.         catch(BadPathException e) {
  1109.             throw new BadPathException("Couldn't stat parent");
  1110.             }
  1111.         JFSuser u = null;
  1112.         try u = FileSystem.getuser(name);
  1113.         catch(BadUserException e) {
  1114.             throw new BadPathException("You don't exist");
  1115.             }
  1116.         if (u.perms(fileinfo).indexOf(mode) < 0)
  1117.             throw new BadPathException("Permission denied");
  1118.         }
  1119.     }
  1120.  
  1121.     // accessfile
  1122.     // Checks if the given file can be accessed by the current user.
  1123.     // If the file is not found, null is returned.
  1124.     // If it is found and can be accessed ok, a pointer to a JFSfile
  1125.     // object is returned.
  1126.     // If the file exists but this user can't access it, an exception is
  1127.     // thrown.
  1128.     JFSfile accessfile(String file, String mode) throws BadPathException
  1129.     {
  1130.     JFSfile info = null;
  1131.     try info = FileSystem.statfile(file);
  1132.     catch(BadPathException e)
  1133.         return null;
  1134.  
  1135.     // Can this user read it?
  1136.     JFSuser u = null;
  1137.     try u = FileSystem.getuser(name);
  1138.     catch(BadUserException e)
  1139.         throw new BadPathException("You don't exist");
  1140.     for(int i=0; i<mode.length(); i++)
  1141.         if (u.perms(info).indexOf(mode.charAt(i)) < 0)
  1142.             throw new BadPathException("Permission denied");
  1143.  
  1144.     return info;
  1145.     }
  1146.  
  1147.     // failreply
  1148.     // Send a failure reply 
  1149.     void failreply(String reason)
  1150.     {
  1151.     Message fail = new Message();
  1152.     fail.add("Reply","Fail");
  1153.     fail.add("Reason",reason);
  1154.     fail.send(out);
  1155.     logreply(fail);
  1156.     }
  1157.  
  1158.     // successreply
  1159.     // Send a success reply
  1160.     void successreply()
  1161.     {
  1162.     Message succ = new Message();
  1163.     succ.add("Reply","Success");
  1164.     succ.send(out);
  1165.     logreply(succ);
  1166.     }
  1167.  
  1168.     // logrequest
  1169.     // Log a received message
  1170.     void logrequest(Message m)
  1171.     {
  1172.     last = System.currentTimeMillis();
  1173.     Date d = new Date(last);
  1174.     String line = "-> ["+d+"] "+name+" "+host+" "+m.find("Request");
  1175.     for(int i=0; i<m.size(); i++)
  1176.         if (!m.name(i).equals("Request"))
  1177.             line += " \""+m.name(i)+"="+m.value(i)+"\"";
  1178.     synchronized(JFSserver.log)
  1179.         JFSserver.log.puts(line);
  1180.  
  1181.     // Add message size
  1182.     int len = messagesize(m);
  1183.     recv += len;
  1184.     JFSserver.brecv.setval(JFSserver.brecv.getval() + len);
  1185.  
  1186.     // Add sent count
  1187.     JFSserver.treq.setval(JFSserver.treq.getval() + 1);
  1188.     }
  1189.  
  1190.     // logreply
  1191.     // Log a sent reply
  1192.     void logreply(Message m)
  1193.     {
  1194.     last = System.currentTimeMillis();
  1195.     Date d = new Date(last);
  1196.     String line = "<- ["+d+"] "+name+" "+host+" "+m.find("Reply");
  1197.     for(int i=0; i<m.size(); i++)
  1198.         if (!m.name(i).equals("Reply"))
  1199.             line += " \""+m.name(i)+"="+m.value(i)+"\"";
  1200.     synchronized(JFSserver.log)
  1201.         JFSserver.log.puts(line);
  1202.  
  1203.     // Add message size
  1204.     int len = messagesize(m);
  1205.     sent += len;
  1206.     JFSserver.bsent.setval(JFSserver.bsent.getval() + len);
  1207.  
  1208.     // Add reply counts
  1209.     String rp = m.find("Reply");
  1210.     if (rp != null) {
  1211.         if (rp.equals("Data"))
  1212.             JFSserver.tdata.setval(JFSserver.tdata.getval() + 1);
  1213.         else if (rp.equals("Success"))
  1214.             JFSserver.tsucc.setval(JFSserver.tsucc.getval() + 1);
  1215.         else
  1216.             JFSserver.tfail.setval(JFSserver.tfail.getval() + 1);
  1217.         }
  1218.     }
  1219.  
  1220.     // messagesize
  1221.     // Compute the size of a message
  1222.     int messagesize(Message m)
  1223.     {
  1224.     int sz = 0;
  1225.     for(int i=0; i<m.size(); i++)
  1226.         sz += (m.name(i).length() + m.value(i).length());
  1227.     byte d[] = m.getdata();
  1228.     if (d != null) sz += d.length;
  1229.     return sz;
  1230.     }
  1231. }
  1232.  
  1233.  
  1234. // ServerProperty
  1235. // A tagged string stored on the server. Each property is identified by
  1236. // a name and a user, and can only be accessed by that user (and root).
  1237. // External properties are used for storing long-term data, such as server
  1238. // configurations and user preferences. Internal properties are used for
  1239. // server status information, and are not saved.
  1240. class ServerProperty
  1241. {
  1242.     String name;
  1243.     String user;
  1244.     String value;
  1245.     boolean external;
  1246.  
  1247.     // Create a new property, read from a string
  1248.     ServerProperty(String line) throws IOException
  1249.     {
  1250.     StringSplitter tok = new StringSplitter(line, ':');
  1251.     if (tok.countTokens() != 4)
  1252.         throw new IOException("ServerProperty format error");
  1253.     name = tok.nextToken();
  1254.     user = tok.nextToken();
  1255.     value = tok.nextToken();
  1256.     external = tok.nextToken().equals("y");
  1257.     }
  1258.  
  1259.     ServerProperty(String n, String u, String v, boolean e)
  1260.     {
  1261.     name = n;
  1262.     user = u;
  1263.     value = v;
  1264.     external = e;
  1265.     }
  1266.  
  1267.     // output
  1268.     // Convert this property back to a string
  1269.     String output()
  1270.     {
  1271.     StringJoiner j = new StringJoiner(':');
  1272.     j.add(name); j.add(user); j.add(value); j.add(external?"y":"n");
  1273.     return j.toString();
  1274.     }
  1275.  
  1276.     // hashCode
  1277.     // For storing this property in the cache hashtable
  1278.     public int hashCode()
  1279.     {
  1280.     return name.hashCode() + user.hashCode();
  1281.     }
  1282.  
  1283.     // getval, setval
  1284.     // Convenience functions for getting and setting the property
  1285.     // value as an integer.
  1286.     int getval() { return Integer.parseInt(value); }
  1287.     void setval(int i) { value = String.valueOf(i); }
  1288. }
  1289.  
  1290.  
  1291. // IdleReaper
  1292. // Cleans up client connections that have been idle for longer than the
  1293. // time specified in the 'Client Timeout' property.
  1294. class IdleReaper extends Thread
  1295. {
  1296.     // run
  1297.     // Loop forever, waking up once every 60 seconds to check on clients.
  1298.     public void run()
  1299.     {
  1300.     while(true) {
  1301.         // Get current time and timeout
  1302.         long now = System.currentTimeMillis();
  1303.         ServerProperty timeoutp =
  1304.             JFSserver.getprop("Client Timeout", "root");
  1305.         if (timeoutp != null) {
  1306.             long timeout = Integer.parseInt(timeoutp.value)*1000;
  1307.  
  1308.             // Check clients
  1309.             synchronized(JFSserver.cons) {
  1310.                 for(int i=0; i<JFSserver.cons.size(); i++) {
  1311.                     ServerClient c =
  1312.                      (ServerClient)JFSserver.cons.
  1313.                                elementAt(i);
  1314.                     if (now - c.last > timeout) {
  1315.                         // Die!
  1316.                         c.shutdown();
  1317.                         }
  1318.                     }
  1319.                 }
  1320.  
  1321.             // Wait for 60 seconds
  1322.             try Thread.sleep(60*1000);
  1323.             catch(InterruptedException e);
  1324.             }
  1325.         }
  1326.     }
  1327. }
  1328.  
  1329.